#import "Common.h"
#import "DataType.h"
#import "LayerGame.h"
#import "LayerResult.h"
#import "InvaderBase.h"
#import "LayerPause.h"
#import "ExplodeScore.h"
#import "Particle.h"

float sEmitStart[2] = { 0.3, 0.4 };			// 初始发射频率
float sEmitLevel[2] = { 0.018, 0.005 };		// 发射频率每级别变化系数
float sEmitSubLevel[2] = { 0, 0.001 };			// 发射频率每子级别变化系数

int sChanceTable[2][INVADERS_COUNT][2] =
{
	{	// Time Mode
		{   20,   0 },		// IT_Normal_1
		{   50,   0 },		// IT_Normal_2
		{   45,   2 },		// IT_Good
		{   80,  -4 },		// IT_Bad
		{  800,   0 },		// IT_Bomb
		{ 1000,   0 },		// IT_Gold
	},
	
	{	// Survival Mode
		{    20,   0 },		// IT_Normal_1
		{    50,   0 },		// IT_Normal_2
		{    50,   2 },		// IT_Good
		{   150,  -5 },		// IT_Bad
		{   500,   5 },		// IT_Bomb
		{  1000,   0 },		// IT_Gold
	}
};	

@implementation SceneGame
-(id)initWithMode:(GameMode)mode
{
	if ((self = [super init])) {
		[self addChild:[[LayerGame alloc] initWithMode:mode]];
		[self autorelease];
	}
	return self;
}
@end

@implementation LayerGame

#pragma mark -
#pragma mark initialize

-(id)initWithMode:(GameMode)mode
{
	if ((self = [super init])) {
		[self autorelease];
		
		mTouch				= NULL;
		mActionBlink	= NULL;
		mActionEmit		= NULL;
		mActionScore	= NULL;
		mActionSpeed	= NULL;
		mActionBless	= NULL;
		mActionAngel	= NULL;
		mActionShield	= NULL;
		mActionSmall	= NULL;

		[self setIsTouchEnabled:YES];

		// 背景
		CCSprite* bg = [CCSprite spriteWithFile:@"background.png"];
		bg.position = gPointCenter;
		bg.opacity = 100;
		[self addChild:bg z:lzo_bg];
		
		// 全屏粒子
		ParticleSpace* par = [ParticleSpace node];
		[self addChild:par];

		// Invaders
		mInvaders = [InvaderManager node];
		[self addChild:mInvaders z:lzo_invaders];

		// Devourer
		mDevourer = [Devourer node];
		mDevourer.position = ccp(gSettingData.startPosX+215,160);
		[self addChild:mDevourer z:lzo_devourer];

		// Score
		mLayerExplodeScore = [CCLayer node];
		[self addChild:mLayerExplodeScore z:lzo_score];

		// Spell slot
		mSpell = [SpellSlot node];
		[self addChild:mSpell z:lzo_spell];
		
		// Buff
		mBuff = [BuffManager node];
		[self addChild:mBuff z:lzo_spell];
		
		// Lifes
		if(mode == GM_Survival) {
			for(int i=0; i<3; i++) {
				mLifes[i] = [CCSprite spriteWithFile:@"devourer.png"];
				mLifes[i].scale = 0.5;
				mLifes[i].position = ccp(380-i*25,302);
				[self addChild:mLifes[i] z:lzo_menu];
				
				mSpriteMiss[i] = [[CCSprite spriteWithFile:@"x.png"] retain];
				mSpriteMiss[i].scale = 0;
				mActionMiss[i] = [[CCSequence actions:
								  [CCScaleTo actionWithDuration:0.3 scale:1],
								  [CCRotateTo actionWithDuration:0.05 angle:-15],
								  [CCRotateTo actionWithDuration:0.05 angle:15],
								  [CCRotateTo actionWithDuration:0.05 angle:-15],
								  [CCRotateTo actionWithDuration:0.05 angle:15],
								  [CCRotateTo actionWithDuration:0.05 angle:-15],
								  [CCRotateTo actionWithDuration:0.05 angle:15],
								  [CCScaleTo actionWithDuration:0.3 scale:0],
								  [CCHide action],NULL] retain];
			}
			mLifeCount = 3;
		}

		// Interface
		mLabelTotalScore = [CCLabelTTF labelWithString:@"0" fontName:gFontName fontSize:24];
		mLabelTotalScore.color = ccYELLOW;
		mLabelTotalScore.anchorPoint = gAnchorLeft;
		mLabelTotalScore.position = ccp(5, 300);
		[self addChild:mLabelTotalScore z:lzo_menu];

		mLabelBonus = [CCLabelTTF labelWithString:@"x 1" fontName:gFontName fontSize:24];
		mLabelBonus.color = ccRED;
		mLabelBonus.anchorPoint = gAnchorLeft;
		mLabelBonus.position = ccp(mLabelTotalScore.contentSize.width+15,300);
		[self addChild:mLabelBonus z:lzo_menu];
		
		mActionBlink = [[CCSequence actions:[CCBlink actionWithDuration:1 blinks:10],[CCShow action],NULL] retain];

		mLabelTime = [CCLabelTTF labelWithString:@"2:00" fontName:gFontName fontSize:24];
		if(mode == GM_Survival)
			mLabelTime.string = @"0:00";
		mLabelTime.color = ccc3(50, 255, 100);
		mLabelTime.anchorPoint = gAnchorRight;
		mLabelTime.position = ccp(475, 300);
		[self addChild:mLabelTime z:lzo_menu];

		// 按钮
		CCMenu* menu = [CCMenu menuWithItems:NULL];
		menu.position = CGPointZero;
		[self addChild:menu z:lzo_menu];
		
		CCMenuItemImage* btnMenu = [CCMenuItemImage itemFromNormalImage:@"btn-pause.png" selectedImage:@"btn-pause-p.png" target:self selector:@selector(callMenu:)];
		if(gSettingData.spellInRight)
			btnMenu.position = ccp(450,30);
		else
			btnMenu.position = ccp(30,30);
		[menu addChild:btnMenu z:1000];

		// 初始化参数
		memset(&gCurScore, 0, sizeof(DataScore));
		gCurScore.maxBonus = gCurScore.bonus = 1;
		gCurScore.gameMode = mode;
		gCurScore.emitFactor = gCurScore.scoreFactor = gCurScore.speedFactor = gCurScore.sizeFactor = 1;
		
		for(int i=0; i<INVADERS_COUNT; i++)
			mChance.addChance(i, sChanceTable[mode][i][0]);

		mTimePassed = 0;
		
		CCSprite* ready = [CCSprite spriteWithFile:@"Ready.png"];
		ready.position = ccp(560,160);
		ready.visible = false;
		
		[ready runAction:[CCSequence actions:
											[CCDelayTime actionWithDuration:0.3],
											[CCShow action],
											[CCMoveTo actionWithDuration:0.2 position:ccp(gSettingData.startPosX+215,160)],
											[CCDelayTime actionWithDuration:0.5],
											[CCMoveTo actionWithDuration:0.2 position:ccp(-80,160)],NULL]];
		[self addChild:ready z:50000];
		
		CCSprite* go = [CCSprite spriteWithFile:@"GO.png"];
		go.position = ccp(520,160);
		go.visible = false;		
		
		[go runAction:[CCSequence actions:
									 [CCDelayTime actionWithDuration:1.7],
									 [CCShow action],
									 [CCMoveTo actionWithDuration:0.2 position:ccp(gSettingData.startPosX+215,160)],
									 [CCDelayTime actionWithDuration:1],
									 [CCMoveTo actionWithDuration:0.2 position:ccp(gSettingData.startPosX+215,345)],NULL]];
		[self addChild:go z:50000];
									 
		[self runAction:[CCSequence actions:
										 [CCDelayTime actionWithDuration:3.1],
										 [CCCallFunc actionWithTarget:self selector:@selector(startGame)],NULL]];
										
		[[SimpleAudioEngine sharedEngine] playBackgroundMusic:MUSIC_BG2 loop:true];

		mLevel = 0;
		mSubLevel = 0;
		mStarted = false;
		mFinished = false;
		mTimeBlinked = false;
		
		mEmitRate = sEmitStart[gCurScore.gameMode];
		mEmitTime = 0;
	}
	return self;
}

-(void)dealloc
{
	[mActionBlink release];
	if(gCurScore.gameMode == GM_Survival) {
		for(int i=0; i<3; i++) {
			[mSpriteMiss[i] release];
			[mActionMiss[i] release];
		}
	}
	[super dealloc];
}

#pragma mark -
#pragma mark schdule

-(void) levelup:(ccTime)dt
{
	mSubLevel++;
	mEmitRate -= sEmitSubLevel[gCurScore.gameMode];
	if(mSubLevel == 10)	{
		mLevel ++;
		if(mLevel == 24) {
			[self unschedule:@selector(levelup:)];
			return;
		}
		
		if(!mActionBless)
			mChance.addChance(IT_Good, sChanceTable[gCurScore.gameMode][IT_Good][0]+sChanceTable[gCurScore.gameMode][IT_Good][1]*mLevel);
		
		if(!mActionAngel)
			mChance.addChance(IT_Bad, sChanceTable[gCurScore.gameMode][IT_Bad][0]+sChanceTable[gCurScore.gameMode][IT_Bad][1]*mLevel);
		
		mEmitRate -= sEmitLevel[gCurScore.gameMode];
		mSubLevel = 0;
	}
	if(mEmitRate < 0.2)
		mEmitRate = 0.2;
}

-(void) update:(ccTime)dt
{
	mTimePassed += dt;

	if(gCurScore.gameMode == GM_Time && mTimePassed >= 120 )
	{
		mTimePassed = 120;
		[self endGame];
	}
	
	mEmitTime += dt;
	float emit = mEmitRate*gCurScore.emitFactor;
	while(mEmitTime >= emit) {
		mEmitTime -= emit;
		int type = mChance.makeDecision(1);
		gCurScore.generateCount[type]++;
		[mInvaders addNew:(InvaderType)type];
	}

	[self updateInvader:dt];
	[mBuff updateBuff:dt];

	mLabelTotalScore.string = [NSString stringWithFormat:@"%s", commaprint(gCurScore.score)];
	mLabelBonus.position = ccp(mLabelTotalScore.contentSize.width+15,300);
	mLabelBonus.string = [NSString stringWithFormat:@"x %d",gCurScore.bonus];
	
	int sec;
	if(gCurScore.gameMode == GM_Time) {
		sec = 120 - (int)mTimePassed;
		if(!mTimeBlinked && sec <= 10) {
			[mLabelTime runAction:
			 [CCRepeatForever actionWithAction:
			  [CCSequence actions:
			   [CCTintTo actionWithDuration:0 red:255 green:0 blue:0],
			   [CCDelayTime actionWithDuration:0.3],
			   [CCTintTo actionWithDuration:0 red:50 green:255 blue:100],
			   [CCDelayTime actionWithDuration:0.3],NULL]]];
			mTimeBlinked = true;
		}
	} else {
		sec =  (int)mTimePassed;
	}
	
	if(sec >= 3600)	{
		int hour = sec / 3600;
		sec %= 3600;
		mLabelTime.string = [NSString stringWithFormat:@"%d:%02d:%02d",hour,sec/60,sec%60];
	}
	else
		mLabelTime.string = [NSString stringWithFormat:@"%d:%02d", sec/60, sec%60];
}

-(void)updateInvader:(ccTime)dt
{
	bool bClearAll = false;
	CCArray* arrInvaders = mInvaders.batch.children;
	InvaderBase* ib;
	
	CCARRAY_FOREACH(arrInvaders, ib) {
		if(ib.active) {
			InvaderType type = ib.iType;
			bool bOut = [ib updateInvader:dt];
			
			if(gCurScore.gameMode == GM_Survival) {
				if(!bOut && (type == IT_Normal_1 || type == IT_Normal_2)) {		// 漏掉了一个
					mLifeCount--;
					[[SimpleAudioEngine sharedEngine] playEffect:EF_MISS];
					// 漏掉时的效果
					CGPoint pos = ib.position;
					if(pos.x > 460)
						pos.x = 460;
					else if(pos.x < 20)
						pos.x = 20;
					if(pos.y > 300)
						pos.y = 300;
					if(pos.y < 20)
						pos.y = 20;
					mSpriteMiss[mLifeCount].position = pos;
					[mSpriteMiss[mLifeCount] runAction:mActionMiss[mLifeCount]];
					[self addChild:mSpriteMiss[mLifeCount] z:lzo_menu];
					
					mLifes[mLifeCount].color = ccc3(50,50,50);
					if(mLifeCount == 0)
						[self endGame];
					break;
				}
			}
			
			if([mDevourer checkCollision:ib]) {
				gCurScore.invadersCount[type]++;
				
				if(type == IT_Bad && gCurScore.gameMode == GM_Survival && !mDevourer.isShieldOn) {
					[[SimpleAudioEngine sharedEngine] playEffect:EF_MISS];
					[self particleEF:0 pos:ib.position];
					[ib destroy];
					[self endGame];
					break;
				}

				if(type == IT_Bomb)
					bClearAll = true;
				else {
					if(mDevourer.isShieldOn && type == IT_Bad)
						[[SimpleAudioEngine sharedEngine] playEffect:EF_COLSHIELD];
					else {
						switch(type) {
							case IT_Good:
								[[SimpleAudioEngine sharedEngine] playEffect:EF_COLGOOD];
								gCurScore.bonus ++;
								gCurScore.maxBonus = MAX(gCurScore.bonus, gCurScore.maxBonus);
								[self blinkBonus];
								break;
							case IT_Normal_1:
							case IT_Normal_2:
								[[SimpleAudioEngine sharedEngine] playEffect:EF_COLNORMAL];
								break;
							case IT_Bad:
								[[SimpleAudioEngine sharedEngine] playEffect:EF_COLBAD];
								break;
							case IT_Gold:
								[[SimpleAudioEngine sharedEngine] playEffect:EF_COLGOLD];
								break;
							default:
								break;
						}

						int score = [ib getScore];
						gCurScore.score += score;
						[self scoreEF:ib.position score:score type:type];

						if(IT_Bad == type) {
							gCurScore.bonus = 1;
							[self blinkBonus];
						}
					}
					[self particleEF:0 pos:ib.position];
				}

				[ib destroy];
				break;
			}
		}
	} // FOR_EACH

	if(bClearAll)
		[self explode:1];
}

#pragma mark -
#pragma mark effect

-(void)explode:(int)type
{
	InvaderBase* ib;
	CCArray* arrInv = mInvaders.batch.children;

	[[SimpleAudioEngine sharedEngine] playEffect:EF_COLBOMB];
	
	[self particleEF:1 pos:mDevourer.position];
	
	CCARRAY_FOREACH(arrInv, ib)
	{
		if(ib.active) {
			switch(type) {
				case 1:	{		// bomb
					if(ib.iType != IT_Bad)
						gCurScore.invadersCount[ib.iType]++;
					int score = ABS([ib getScore]);
					if(score != 0) {
						gCurScore.score += score;
						[self scoreEF:ib.position score:score type:IT_Normal_1];
					}
					[self particleEF:0 pos:ib.position];
					[ib destroy];
					break;
				}
				case 2: {		// bless
					if(ib.iType == IT_Bad) {
						int score = -[ib getScore];
						gCurScore.score += score;
						[self scoreEF:ib.position score:score type:IT_Normal_1];
						[self particleEF:0 pos:ib.position];
						[ib destroy];
					}
					break;
				}
			}
		}
	}
}

-(void)blinkBonus
{
	[mLabelBonus stopAllActions];
	CCAction* action = [mActionBlink copy];
	[mLabelBonus runAction:action];
	[action release];
}

-(void)particleEF:(int)type pos:(CGPoint)pos
{
	ParticleExplode* par = [[ParticleExplode alloc] initWithExSize:type];
	par.position = pos;
	[self addChild:par z:lzo_score];
}

-(void)scoreEF:(CGPoint)pos score:(int)score type:(InvaderType)type
{
	[mLayerExplodeScore addChild:[[ExplodeScore alloc] initWithPos:pos score:score type:type]];
}

#pragma mark -
#pragma mark flow control

-(void)callMenu:(CCMenuItem*)sender
{
	if(!mStarted)
		return;
	
	[[SimpleAudioEngine sharedEngine] playEffect:SOUND_BTN01];
	[self onExit];
	[self.parent addChild:[LayerPause node] z:10000];
}

-(void)updatePos
{
	CGPoint pos = [self convertTouchToNodeSpace:mTouch];
	if(gSettingData.operationType == 1)
		[mDevourer setDevourerPos:pos];
	else {
		CGPoint devourerPos = mDevourer.position;
		[mDevourer setDevourerPos:ccpAdd(devourerPos, ccpSub(pos, mLastTouchPos))];
		mLastTouchPos = pos;
	}
}

-(void)startGame
{
	mStarted = true;
	[self schedule:@selector(levelup:) interval:1];
	[self scheduleUpdate];
}

-(void)endGame
{
	mFinished = true;
	gCurScore.lastInSurvival = mTimePassed;
	
	[[SimpleAudioEngine sharedEngine] playEffect:EF_GAMEFINISH];
	
	// GAME OVER
	[self unschedule:@selector(levelup)];
	[self unscheduleUpdate];
	
//	CCLOG(@"Game Finish:N1[%d],N2[%d],Good[%d],Bad[%d],Bomb[%d],Gold[%d]",
//		   gCurScore.generateCount[IT_Normal_1],gCurScore.generateCount[IT_Normal_2],gCurScore.generateCount[IT_Good],
//		   gCurScore.generateCount[IT_Bad],gCurScore.generateCount[IT_Bomb],gCurScore.generateCount[IT_Gold]);
	
	CCSprite* spriteFinish = [CCSprite spriteWithFile:@"Finish.png"];
	int x = 210+gSettingData.startPosX;
	spriteFinish.position = ccp(x,250);
	[self addChild:spriteFinish z:500];
	[spriteFinish runAction:
	 [CCSequence actions:
	  [CCEaseBounceOut actionWithAction:[CCMoveTo actionWithDuration:0.5 position:ccp(x,160)]],
	  [CCDelayTime actionWithDuration:1],
	  [CCCallFunc actionWithTarget:self selector:@selector(callResult)],NULL]];
}

-(void)callResult
{
	for(int i=0; i<SPELLSLOT_COUNT; i++)
		gGlobalData.spells[i] = [mSpell spell:i].sType;
	
	[[CCDirector sharedDirector] replaceScene:
	 [CCTransitionFade transitionWithDuration:0.5 scene:[SceneResult node] withColor:ccWHITE]];
	[[SimpleAudioEngine sharedEngine] playBackgroundMusic:MUSIC_BG1 loop:true];
}

#pragma mark -
#pragma mark cast spell
-(void)castSpell:(Spell*)spell
{
	
	if(spell.sType == ST_Bomb) {
		[self explode:1];
		return;
	}
	else if(spell.sType == ST_Bless)
		[self explode:2];
	else if(spell.sType == ST_Angel)
		[self explode:1];
	else
		[[SimpleAudioEngine sharedEngine] playEffect:EF_CAST];

	[self setState:spell];
	[mBuff addBuff:spell];
}

-(void)setState:(Spell*)spell;
{
	switch(spell.sType) {
		case ST_Shield:
			[self restoreShield];
			[mDevourer enableShield];
			mActionShield = [CCSequence actions:[CCDelayTime actionWithDuration:spell.duration],
											[CCCallFunc actionWithTarget:self selector:@selector(restoreShield)], NULL];
			[self runAction:mActionShield];
			break;
		case ST_Small:
			[self restoreSmall];
			gCurScore.sizeFactor = 0.7;
			mActionSmall = [CCSequence actions:[CCDelayTime actionWithDuration:spell.duration],
											[CCCallFunc actionWithTarget:self selector:@selector(restoreSmall)], NULL];
			[self runAction:mActionSmall];
			break;
		case ST_Angel:
			[self restoreBless];
			mChance.addChance(IT_Good, 5);
			mActionBless = [CCSequence actions:[CCDelayTime actionWithDuration:spell.duration],
											[CCCallFunc actionWithTarget:self selector:@selector(restoreBless)], NULL];
			[self runAction:mActionBless];
			break;
		case ST_Bless:
			[self restoreAngel];
			mChance.addChance(IT_Bad, -1);
			mActionAngel = [CCSequence actions:[CCDelayTime actionWithDuration:spell.duration],
											[CCCallFunc actionWithTarget:self selector:@selector(restoreAngel)], NULL];
			[self runAction:mActionAngel];
			break;
		case ST_Double:
			[self restoreDouble];
			gCurScore.scoreFactor = 2;
			mActionScore = [CCSequence actions:[CCDelayTime actionWithDuration:spell.duration],
											[CCCallFunc actionWithTarget:self selector:@selector(restoreDouble)], NULL];
			[self runAction:mActionScore];
			break;
		case ST_Burst:
			[self restoreBurst];
			gCurScore.emitFactor = 0.7;;
			mActionEmit = [CCSequence actions:[CCDelayTime actionWithDuration:spell.duration],
										 [CCCallFunc actionWithTarget:self selector:@selector(restoreBurst)], NULL];
			[self runAction:mActionEmit];
			break;
		case ST_Rare:
			[self restoreBurst];
			gCurScore.emitFactor = 1.5;
			mActionEmit = [CCSequence actions:[CCDelayTime actionWithDuration:spell.duration],
										 [CCCallFunc actionWithTarget:self selector:@selector(restoreBurst)], NULL];
			[self runAction:mActionEmit];
			break;
		case ST_Freeze:
			[self restoreFreeze];
			gCurScore.speedFactor = 0.5;
			mActionSpeed = [CCSequence actions:[CCDelayTime actionWithDuration:spell.duration],
											[CCCallFunc actionWithTarget:self selector:@selector(restoreFreeze)], NULL];
			[self runAction:mActionSpeed];
			break;
		default:
			break;
	}
}

-(void)restoreShield
{
	[mDevourer disableShield];
	if(mActionShield)
		[self stopAction:mActionShield];
	mActionShield = NULL;
}

-(void)restoreSmall
{
	gCurScore.sizeFactor = 1;
	if(mActionSmall)
		[self stopAction:mActionSmall];
	mActionSmall = NULL;
}

-(void)restoreAngel
{
	mChance.addChance(IT_Bad, sChanceTable[gCurScore.gameMode][IT_Bad][0]+sChanceTable[gCurScore.gameMode][IT_Bad][1]*mLevel);
	if(mActionAngel)
		[self stopAction:mActionAngel];
	mActionAngel = NULL;
}

-(void)restoreBless
{
	mChance.addChance(IT_Good, sChanceTable[gCurScore.gameMode][IT_Good][0]+sChanceTable[gCurScore.gameMode][IT_Good][1]*mLevel);
	if(mActionBless)
		[self stopAction:mActionBless];
	mActionBless = NULL;	
}

-(void)restoreBurst
{
	gCurScore.emitFactor = 1;
	if(mActionEmit)
		[self stopAction:mActionEmit];
	mActionEmit = NULL;
}

-(void)restoreDouble
{
	gCurScore.scoreFactor = 1;
	if(mActionScore)
		[self stopAction:mActionScore];
	mActionScore = NULL;
}

-(void)restoreFreeze
{
	gCurScore.speedFactor = 1;
	if(mActionSpeed)
		[self stopAction:mActionSpeed];
	mActionSpeed = NULL;
}

#pragma mark -
#pragma mark Delegate
-(void)registerWithTouchDispatcher
{
	[[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0];
}

-(void)ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
	if(!mStarted || mFinished)
		return;
	
	UITouch* touch;
	if(!mTouch) {		// 判断按下
		for(touch in touches) {
			CGPoint pos = [self convertTouchToNodeSpace:touch];
			if(gSettingData.operationType == 1) {
				if([mDevourer containPoint:pos]) {
					mTouch = touch;
					[mDevourer setDevourerPos:pos];
					break;
				}
			} else {
				mTouch = touch;
				mLastTouchPos = pos;
				break;
			}
		}
	}
	
	// 判断魔法
	for(touch in touches)	{
		int idx = [mSpell checkClick:touch];
		if(idx >= 0) {
			Spell* spell = [mSpell spell:idx];
			if(spell.sType != ST_None) {
				gCurScore.spellCastCount ++;
				[self castSpell:spell];
				[spell cast];
				break;
			}
		}
	}
}

- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
	if(!mStarted || mFinished)
		return;

	if(mTouch && [touches containsObject:mTouch])
		[self updatePos];
}

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	if(!mStarted || mFinished)
		return;
	
	if(mTouch && [touches containsObject:mTouch])	{
		[self updatePos];
		mTouch = NULL;
	}
}

- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
	if(!mStarted || mFinished)
		return;

	if(mTouch && [touches containsObject:mTouch])	{
		[self updatePos];
		mTouch = NULL;
	}
}

@end